package com.wugh.fast.kernel.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.func.VoidFunc0;
import cn.hutool.core.util.PageUtil;
import com.wugh.fast.kernel.core.exception.FastBusinessException;
import com.wugh.fast.kernel.util.recursive.DefaultRecursive;
import com.wugh.fast.kernel.util.recursive.Recursive;
import com.wugh.fast.kernel.util.recursive.SingleRecursive;
import com.wugh.fast.kernel.util.recursive.StreamRecursive;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.*;
import java.util.stream.Stream;

/**
 * 说明: 递归工具
 * <pre>
 *     递归中的全部方法都限制了递归次数,如果大于1000次的会抛出异常. 防止递归异常
 *
 * </pre>
 *
 * @author wugh
 * @date 2019-11-27 17:52
 */
@Slf4j
public class RecursiveUtils {

    private RecursiveUtils() {
    }

    public static <T> void splitCollection(Collection<T> collection, Consumer<Collection<T>> splitHandler, int step) {
        if (CollectionUtil.isEmpty(collection)) {
            return;
        }
        if (step <= 0) {
            throw new FastBusinessException("步数必须大于0");
        }
        int size = collection.size();
        int page = PageUtil.totalPage(size, step);
        if (page == 1) {
            splitHandler.accept(collection);
        } else {
            List<T> list;
            if (collection instanceof List) {
                list = (List<T>) collection;
            } else {
                list = new ArrayList<>(collection);
            }
            Stream.iterate(0, i -> i + step).limit(page).forEach(start -> {
                splitHandler.accept(list.subList(start, Math.min(start + step, size)));
            });
        }
    }

    /**
     * 创建默认的递归函数
     *
     * @param function
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> Recursive<T, R> build(Function<T, R> function, BiPredicate<T, R> test, BiFunction<T, R, T> map) {
        return new DefaultRecursive<T, R>().function(function).test(test).map(map);
    }

    /**
     * 创建简单的递归函数
     *
     * @param function
     * @param <T>
     * @return
     */
    public static <T> Recursive<T, T> buildSingle(Function<T, T> function, BiPredicate<T, T> test) {
        return new SingleRecursive<T>().function(function).test(test);
    }

    /**
     * 无任何入参的递归
     *
     * @param function
     * @param test
     * @return
     */
    public static Recursive<Void, Void> buildVoid(VoidFunc0 function, Supplier<Boolean> test) {
        return new DefaultRecursive<Void, Void>().function((t) -> {
            try {
                function.call();
            } catch (Exception e) {
                ExceptionUtil.wrapAndThrow(e);
            }
            return null;
        }).test((t, r) -> test.get());
    }

    /**
     * @param childrenGetter
     * @param <T>
     * @return
     */
    public static <T> StreamRecursive<T, T> buildStream(Function<T, Collection<T>> childrenGetter) {
        return new StreamRecursive<>(childrenGetter);
    }

    /**
     * @param childrenGetter
     * @param mapFun
     * @param <T>
     * @return
     */
    public static <T, R> StreamRecursive<T, R> buildStream(Function<T, Collection<T>> childrenGetter, Function<StreamRecursive.Box<T, R>, R> mapFun) {
        return new StreamRecursive<>(childrenGetter, mapFun);
    }

    /**
     * 递归处理
     *
     * @param func      方法
     * @param predicate 判断是否进入下一次递归
     */
    public static void exec(VoidFunc0 func, Supplier<Boolean> predicate) {
        buildVoid(func, predicate).exec(null);
    }

    /**
     * 递归处理集合并转化成流
     * <pre>
     *     自动判断是否还能获取子集合作为递归判断条件
     * </pre>
     *
     * @param collection     集合对象
     * @param childrenGetter 入参处理对象,获取子集合的函数
     * @param <T>            泛型
     * @return 返回全部的流(包括传入的集合)
     */
    public static <T> Stream<T> exec2Stream(Collection<T> collection, Function<T, Collection<T>> childrenGetter) {
        return buildStream(childrenGetter).exec(collection);
    }

    /**
     * 递归处理
     * <pre>
     *     不处理输入,默认把函数结果作为下一次递归入参
     * </pre>
     *
     * @param value     第一次执行的递归方法
     * @param func      递归执行方法
     * @param predicate 判断是否进入下一次递归
     * @param <T>       入参泛型和结果泛型
     * @return 返回递归结果
     */
    public static <T> T exec(T value, Function<T, T> func, Predicate<T> predicate) {
        return exec(value, func, predicate, (prev, current) -> current);
    }

    /**
     * 递归处理
     * <pre>
     *     不处理输入,默认把函数结果作为下一次递归入参
     * </pre>
     *
     * @param value     第一次执行的递归方法
     * @param func      递归执行方法
     * @param predicate 判断是否进入下一次递归
     * @param reduce    结果合并,会传入之前的结果和当前的结果(注意第一次执行递归方法不会执行)(注意第一次执行递归方法不会执行)
     * @param <T>       入参泛型和结果泛型
     * @return 返回合并处理后的结果
     */
    public static <T> T exec(T value, Function<T, T> func, Predicate<T> predicate, BinaryOperator<T> reduce) {
        return exec(value, func, (t, r) -> predicate.test(r), (t, r) -> r, reduce);
    }

    /**
     * 递归处理
     *
     * @param value     第一次执行的递归方法
     * @param func      递归执行方法
     * @param predicate 判断是否进入下一次递归
     * @param mapFunc   下一次循环的入参处理方法
     * @param <T>       入参泛型
     * @param <R>       结果泛型
     * @return 返回递归的最后一次结果
     */
    public static <T, R> R exec(T value, Function<T, R> func, BiPredicate<T, R> predicate, BiFunction<T, R, T> mapFunc) {
        return exec(value, func, predicate, mapFunc, (prev, current) -> current);
    }

    /**
     * 递归处理
     *
     * @param value     第一次执行的递归方法
     * @param func      递归执行方法
     * @param predicate 判断是否进入下一次递归
     * @param mapFunc   下一次循环的入参处理方法
     * @param reduce    结果合并,会传入之前的结果和当前的结果(注意第一次执行递归方法不会执行)(注意第一次执行递归方法不会执行)
     * @param <T>       入参泛型
     * @param <R>       结果泛型
     * @return 返回合并处理后的结果
     */
    public static <T, R> R exec(T value, Function<T, R> func, BiPredicate<T, R> predicate, BiFunction<T, R, T> mapFunc, BinaryOperator<R> reduce) {
        return build(func, predicate, mapFunc).reduce(reduce).exec(value);
    }
}
